#pragma  once

#define Iskandar
// Variant 45
#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
#include <vector>
#include <time.h>
#include <mpi.h>

namespace           Constant {
	const double    EPS_5 = 0.00001;

	
	const int       X_GRID_SIZE = 101;
	const int       T_GRID_SIZE = 3;
	const int       NUM_OF_EQUATIONS = X_GRID_SIZE - 2;
	const int       TRIDIAGONAL_MATRIX_SIZE = NUM_OF_EQUATIONS * (NUM_OF_EQUATIONS + 1);
	const double    X_LOWER_BOUND = 0.0;
	const double    X_UPPER_BOUND = 1.0;

	const double    T_LOWER_BOUND = 0.0;
	const double    T_UPPER_BOUND = 1.0;

	const double    H = (X_UPPER_BOUND - X_LOWER_BOUND) / (X_GRID_SIZE - 1.0);
	const double    TAU = (T_UPPER_BOUND - T_LOWER_BOUND) / (T_GRID_SIZE - 1.0);

	const double NUM_OF_ITTERATIONS = 2;

#ifdef Markov
	const double    a = 1;
	const double    B = 1;
	const double    A = 1;
	const double    C = 1;
	const double    SIGMA = TAU / (4 * (H * H));
#endif // Markov
#ifdef Sherenk
	const double    a = 2;
	const double    b = 2;
	const double    A = 3;
	const double    B = 3;
	const double    SIGMA = TAU / (4 * (H * H));
#endif // Sherenk
#ifdef Iskandar
	const double    a = 0.5;
	const double    b = 0.8;
	const double    A = 1.5;
	const double    B = 1.9;
	const double    SIGMA = TAU / (4 * (H * H));
#endif // Iskandar

	// euler
	const double    e = M_E;
}

class               Timer {
public:
	Timer() : _current_start(0) {};
	inline void     start() {
		if (!_current_start)
			_current_start = clock();
	};
	inline void     stop() {
		if (_current_start) {
			timers.push_back(clock() - _current_start);
			_current_start = 0;
		}
	};
	inline void     restart() {
		stop();
		start();
	};
	inline float    get(int idx) {
		return ((float)timers[idx]) / CLOCKS_PER_SEC;
	}
	inline int      size() {
		return timers.size();
	}
private:
	clock_t         _current_start;
	std::vector<clock_t> timers;
};
static void print(const char str[]){
	std::cout << "\n Error\n" << str << std::endl;
}
class               Vector {
public:
	Vector(int length);
	virtual         ~Vector();
	double          get(int i) const;
	void            set(int i, double value);
	void            output() const;
	void            operator=       (Vector &v);

	inline int      length() const { return _length; }
	inline double*      data()const{ return _data; };

private:
	int             _length;
	double          *_data;
};

class               Matrix {
public:
	Matrix(int rows, int columns);
	virtual         ~Matrix();
	double          get(int i, int j) const;
	void            set(int i, int j, double value);
	void            output() const;
	void            calculateError(const Matrix &numerical, const Matrix &accurate);
	void            calculateAbsoluteError(const Matrix &numerical, const Matrix &accurate);
	double          average() const;

	void            operator=       (Matrix &m);

	void            serialize(const char *filename) const;

	inline int      rows() const { return _rows; }
	inline int      columns() const { return _columns; }
	inline double *     data() const { return _data; }

	static void     thomasAlgorithm(const Matrix &matrix, Vector &result);
	static void     thomasLeft(const Matrix &matrix, Vector &result);
	static void	    progon(const Matrix &matrix, Vector &result);
private:
	int             _rows;
	int             _columns;
	double          *_data;
};